package org.jsbd.boss.task;

import edu.hziee.common.queue.DelayExecuteBuffer;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.dao.IChannelRetentConfigDAO;
import org.jsbd.boss.dao.IDmChannelRetentDAO;
import org.jsbd.boss.dao.IRetentLogDAO;
import org.jsbd.boss.domian.IpRange;
import org.jsbd.boss.domian.channel.*;
import org.jsbd.boss.enums.ApkBaseEnum;
import org.jsbd.boss.enums.NetworkTypeEnum;
import org.jsbd.boss.enums.RetentStatusEnum;
import org.jsbd.boss.enums.TaskTypeEnum;
import org.jsbd.boss.service.*;
import org.jsbd.boss.service.impl.CacheParamManager;
import org.jsbd.boss.service.impl.TaskLuaManager;
import org.jsbd.boss.service.impl.TaskUtil;
import org.jsbd.boss.util.BeanCopierUtil;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.jsbd.boss.util.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.util.*;

/**
 * 缓存留存任务
 *
 * @Date 2018/9/17 11:01
 * @Author xupengcheng [xupengcheng@zhexinit.com]
 */
public class RetentDataPoolTask implements Runnable {

    public static final Logger logger = LoggerFactory.getLogger(RetentDataPoolTask.class);

    private List<RetentRecordVo> recordList;

    private RedisClient redisClient;

    private RedisClient redisClientTemp;

    private TaskLuaManager taskLuaManager;


    private IRetentFilterConfigService retentFilterConfigService;

    private IScriptService scriptService;

    private IDmChannelRetentDAO channelRetentDAO;

    private DelayExecuteBuffer<ApkRetentLogVo> retentFillExtimesBuffer;

    private LogManager logManager;

    private CacheParamManager cacheParamManager;

    private IChannelApkInfoService channelApkInfoService;

    private IRetentFilterService retentFilterService;

    private ITaskPoolService taskPoolService;

    private IApkSimulatorService apkSimulatorService;

    private IUserClientInfoLogService userClientInfoLogService;

    private ApkManager apkManager;

    private IProvinceService provinceService;

    private long dayOfTime;

    public RetentDataPoolTask(List<RetentRecordVo> recordList) {
        this.recordList = recordList;
        scriptService = SpringContextUtils.getBean("scriptService");
        retentFilterConfigService = SpringContextUtils.getBean(IRetentFilterConfigService.class);
        channelRetentDAO = SpringContextUtils.getBean(IDmChannelRetentDAO.class);
        retentFillExtimesBuffer = SpringContextUtils.getBean("retentFillExtimesBuffer");
        channelApkInfoService = SpringContextUtils.getBean(IChannelApkInfoService.class);
        retentFilterService = SpringContextUtils.getBean(IRetentFilterService.class);
        taskLuaManager = SpringContextUtils.getBean(TaskLuaManager.class);
        redisClient = SpringContextUtils.getBean("redisClient");
        redisClientTemp = SpringContextUtils.getBean("redisClientTemp");
        cacheParamManager = SpringContextUtils.getBean(CacheParamManager.class);
        taskPoolService = SpringContextUtils.getBean(ITaskPoolService.class);
        userClientInfoLogService = SpringContextUtils.getBean(IUserClientInfoLogService.class);
        apkSimulatorService = SpringContextUtils.getBean(IApkSimulatorService.class);
        apkManager = SpringContextUtils.getBean(ApkManager.class);
        logManager = SpringContextUtils.getBean(LogManager.class);
        provinceService = SpringContextUtils.getBean(IProvinceService.class);
    }


    private long getRetentLockTime() {
        long expireTime = 120;
        Object apkTimeStr = cacheParamManager.getParamValue(CacheKey.RETENT_TASK_DATA_LOCK);
        if (apkTimeStr != null) {
            expireTime = Long.parseLong(apkTimeStr.toString());
        }
        return expireTime;
    }

    private int getMaxSize() {
        Object maxSize = cacheParamManager.getParamValue(CacheKey.R_T_Q_M_KEY);
        if (maxSize != null) {
            return Integer.parseInt(maxSize.toString());
        }
        return 10;
    }

    private int lockTime() {
        int lockTime = 60;

        Object paramValue = cacheParamManager.getParamValue(CacheKey.RETENT_QUERY_LOCK_TIME);
        if (paramValue != null) {
            lockTime = Integer.parseInt(paramValue.toString());
        }

        return lockTime;
    }

    @Override
    public void run() {
        if (recordList == null || recordList.isEmpty()) {
            return;
        }
        dayOfTime = DateUtil.getDaysOfTime();
        int maxSize = getMaxSize();
        String apkLockKey = CacheKey.AR_TASK_LOCK + recordList.get(0).getApkId();
        if (redisClient.evalSetNx(apkLockKey, getRetentLockTime())) {
            try {
                long start = System.currentTimeMillis();
                for (RetentRecordVo record : recordList) {
                    //包加锁 不按天加锁
                    updateRetentPool(record, maxSize);
                }
                logManager.printLog(logger, "留存队列更新时间 {} 天 {} 耗时 {}", new Object[]{recordList.get(0).getMark(), dayOfTime, System.currentTimeMillis() - start});
            } catch (Exception e) {
                logger.error("留存队列更新报错 {} {}", recordList.get(0).getMark(), dayOfTime);
            } finally {
                redisClient.remove(apkLockKey);
            }
        }
    }


    private void updateRetentPool(RetentRecordVo record, int maxSize) throws ParseException {

        Integer dayOrMonth = record.getDayOrMonth();
        Integer taskType = record.getTaskType();
        if (taskType == null || taskType != 1) {
            return;
        }
        Integer execTimes = record.getExecTimes();
        String apk = record.getMark();
        Integer apkId = record.getApkId();
        String taskQueueKey = CacheKey.R_T_Q_D_KEY2 + dayOfTime + apkId + taskType + dayOrMonth + execTimes;
        Integer size = taskLuaManager.getTempQueueSize(new String[]{taskQueueKey})[0];
        if (size != null) {
            if (size >= maxSize / 2) {
                logManager.printLog(logger, "更新留存队列量已经足够 {} {}", new Object[]{apk, taskQueueKey});
                return;
            } else if (size >= taskLuaManager.getLeftNumOfDayTimes(apk, dayOrMonth, execTimes, dayOfTime)) {
                logManager.printLog(logger, "更新留存队列已经完成了不需要再获取留存任务 {} {}", new Object[]{apk, dayOrMonth});
                return;
            }
        }
        long start = System.currentTimeMillis();
        List<String> taskList = new ArrayList<>();
        int hasGetSize = 0;
        Integer preDay = DateUtil.getPreDay(dayOrMonth.toString());
        ApkRetentConfig config = channelApkInfoService.selectRetentApkConfig(apk, preDay);
        if (config == null) {
            return;
        }
        List<RetentTaskVo>  retentList = getRetentTaskList(config, dayOrMonth, maxSize, execTimes);
        //留存脚本
        String script = scriptService.randomScript(apk, TaskTypeEnum.RETENT_TASK.getType());
        if (StringUtils.isNotBlank(script)) {
            config.setRetentScriptPath(script);
        }
        if (retentList != null && retentList.size() > 0) {
            hasGetSize = retentList.size();
            String sysVer = apkSimulatorService.getVer(apk);
            boolean newSimulator = apkManager.checkNewSimulator(sysVer);
            for (RetentTaskVo taskVo : retentList) {
                //如果file1name 是空的 并且 file4name也是空的不下发 20181115
                String file4Name = taskVo.getFile4Name();
                String file1Name = taskVo.getFile1Name();
                if (StringUtils.isBlank(file1Name) && StringUtils.isBlank(file4Name)) {
                    continue;
                }
                taskVo.setNewUser(newSimulator);
                boolean isNew = false;
                if (!newSimulator) {
                    Object isExists = redisClientTemp.get(CacheKey.A_RETENT_APK_RECORD + taskVo.getImsi() + ":" + apkId);
                    if (isExists != null) {
                        isNew = true;
                    }
                }
                if (newSimulator || isNew) {
                    taskVo.setCurrentDate(dayOrMonth + "");
                    initRetentTaskVo(taskVo, config);
                    IpRange ipRange = provinceService.getProvinceInfo(taskVo.getImsi(), taskVo.getMobile());
                    Integer provinceId = ipRange.getProvinceId();
                    if (provinceId == null) {
                        provinceId = -1;
                    }
                    taskVo.setProvince(provinceId);
                    taskList.add(TaskUtil.initNewRetentTask(taskVo).toJSONString());
                    logManager.printLog(logger, "更新留存任务队列走新的流程 {} {} {} ", new Object[]{apk, dayOrMonth, taskVo.getImsi()});
                    continue;
                }
                //否则查询用户信息  2018-11-13 14:15:42 开始就没有收集用户信息了
                UserClientInfo clientInfo = getUserClientInfo(taskVo);
                if (clientInfo != null) {
                    BeanCopierUtil.UserClient2RetentTaskVoBeanCopier.copy(clientInfo, taskVo, null);
                    taskVo.setCurrentDate(dayOrMonth + "");
                    initRetentTaskVo(taskVo, config);
                    String taskData = TaskUtil.initRetentTaskJSON(taskVo).toJSONString();
                    taskList.add(taskData);
                    OssTask ossTask = new OssTask(taskData, taskVo.getImsi(), TaskTypeEnum.ADD_TASK.getType(), apkId, taskVo.getCurrentDate(), apk);
                    taskPoolService.execRetentUploadTask(ossTask);
                } else {
                    logManager.printLog(logger, "更新留存任务队列没有获取到用户信息 {} {} {} ", new Object[]{apk, dayOrMonth, taskVo.getImsi()});
                }
            }
            // 更新留存数据
            if (taskList.size() > 0) {
                Boolean flag = taskLuaManager.addTempQueue(taskQueueKey, taskList);
                if (flag != null && flag) {
                    logManager.printLog(logger, "更新留存任务队列1 {} 天{} 留存队列 {} 任务 获取到的任务数量{} 成功{}", new Object[]{apk, dayOrMonth, taskQueueKey, hasGetSize, taskList.size() - 2});
                } else {
                    logManager.printLog(logger, "更新留存任务队列2 {} 天{}  留存队列 {} 任务 获取到的任务数量{} 任务数量{} 没成功", new Object[]{apk, dayOrMonth, taskQueueKey, hasGetSize, taskList.size() - 2});
                }
                long end = System.currentTimeMillis();
                logManager.printLog(logger, "获取留存任务时间 {} {} {} 秒", new String[]{apk, dayOrMonth.toString(), String.valueOf((end - start) / 1000)});
            } else {
                logManager.printLog(logger, "更新留存任务队列3  {} 天{}  留存队列 {} 任务 获取到的任务数量{} 任务没成功", new Object[]{apk, dayOrMonth, taskQueueKey, hasGetSize});
            }
        } else {
            logManager.printLog(logger, "更新留存任务队列4 {} 任务数量为0", new Object[]{taskQueueKey});
        }
    }


    private void initRetentTaskVo(RetentTaskVo taskVo, ApkRetentConfig config) {
        String apk = config.getMark();
        Integer apkId = config.getId();
        taskVo.setTaskType(TaskTypeEnum.RETENT_TASK.getType());
        taskVo.setMark(apk);
        taskVo.setRetentScriptPath(config.getRetentScriptPath());
        taskVo.setApkPath(config.getApkPath());
        taskVo.setRetentJarPath(config.getRetentJarPath());
        if (taskVo.getLongitude() == null) {
            taskVo.setLongitude("0.00");
        }
        if (taskVo.getLatitude() == null) {
            taskVo.setLatitude("0.00");
        }
        if (taskVo.getNetworkType() == null) {
            taskVo.setNetworkType(NetworkTypeEnum.WIFI.getType());
        }
        taskVo.setFileFilter(config.getFileFilter());
        taskVo.setRegisterActivity(config.getRegisterActivity());
        taskVo.setCarrier(ClientManager.checkImsi(taskVo.getImsi()));
        taskVo.setApkId(apkId);
        taskVo.setBase(config.getBase());
        taskVo.setSysPatch(config.getSysPatch());
        taskVo.setDisplayCallers(config.getDisplayCallers());
        taskVo.setSelfFile(config.getSelfFile());
        taskVo.setMaxTaskTime(config.getMaxTaskTime());
        taskVo.setLogLevel(config.getLogLevel());
    }


    /**
     * updateRetentRecord:更新留存执行记录
     *
     * @param apk
     * @param imsi
     * @param day
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private int updateRetentRecord(String apk, String imsi, String day, Integer times) {
        RetentTaskVo taskLog = new RetentTaskVo();
        taskLog.setImsi(imsi);
        taskLog.setMark(apk);
        taskLog.setCurrentDate(day);
        taskLog.setCurrentexetimes(times);
        taskLog.setStatus(RetentStatusEnum.EXECING.getType());
        return channelRetentDAO.updateRetentTimes(taskLog);
    }


    /**
     * getRetentTaskList:获取留存任务
     *
     * @param config
     * @param day
     * @param length
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private List<RetentTaskVo> getRetentTaskList(ApkRetentConfig config, Integer day, Integer length, int execTimes) {
        String apk = config.getMark();
        Integer apkId = config.getId();
        //优先级---行为
        TreeMap<Integer, List<Integer>> priorityActionMap = getActionType(apkId);
        if (priorityActionMap != null && !priorityActionMap.isEmpty()) {
            String currDay = DateUtil.getCurrentDateStr();
            // 查询有没有量 不是当天的量必须大于0
            if (!currDay.equals(day.toString())) {
                int nofilterNum = 0;
                List<MarkDayTask> markDayTasks = retentFilterService.selectRetentMarkDayTask(day, apk);
                int filterTotal = getFileterRetentNum(markDayTasks, priorityActionMap);
                if (filterTotal > 0) {
                    int maxTotal = getApkMaxTotalOfDay(apk, day);
                    if (filterTotal >= maxTotal) {
                        // 如果限制的量多强制使用限制的
                        return getRetentFilterList(apk, day, length, config, priorityActionMap, markDayTasks, execTimes);
                    } else {
                        // 没有限制的留存最多能做的量
                        nofilterNum = maxTotal - filterTotal;
                    }
                    List<RetentTaskVo> list = getRetentFilterList(apk, day, length, config, priorityActionMap, markDayTasks, execTimes);
                    if (list != null && list.size() > 0) {
                        // 查询到了就做，否则做没有优先级的
                        return list;
                    } else {
                        // 没有限制的留存最多能做的量
                        String key = CacheKey.NO_FILTER_RETENT_REORD + dayOfTime;
                        String field = apk + day;
                        Object hasExecTotal = redisClient.evalHget(key, field);
                        int nofilterLeftNum = 0;
                        int hasExec = 0;
                        if (hasExecTotal != null) {
                            hasExec = Integer.parseInt(hasExecTotal.toString());
                        }
                        nofilterLeftNum = nofilterNum - hasExec;
                        if (nofilterLeftNum > 0) {
                            if (nofilterLeftNum < length) {
                                length = nofilterLeftNum;
                            }
                            List<RetentTaskVo> noFilterList = selectNofilterRetentTaskList(config, day, length, execTimes);
                            if (noFilterList != null && noFilterList.size() > 0) {
                                redisClient.evalHincrby(key, field, noFilterList.size(), DateUtil.leftTimeToMidNight() / 1000);
                            }
                            return noFilterList;
                        } else {
                            return null;
                        }
                    }
                } else {
                    return selectNofilterRetentTaskList(config, day, length, execTimes);
                }
            } else {
                // 当天的强制做留存限制的
                return getRetentFilterList(apk, day, length, config, priorityActionMap, null, execTimes);
            }
        } else {
            return selectNofilterRetentTaskList(config, day, length, execTimes);
        }
    }

    /**
     * getRetentFilterList: 暂时不支持多次的
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private List<RetentTaskVo> getRetentFilterList(String apk, Integer day, Integer length, ApkRetentConfig config, TreeMap<Integer, List<Integer>> priorityActionMap, List<MarkDayTask> markDayTasks, Integer execTimes) {

        Integer retent24Filter = config.getRetent24Filter();
        List<RetentTaskVo> targetList = new ArrayList<>();
        int filterTotal = getFileterRetentNum(markDayTasks, priorityActionMap);
        int maxTotal = getApkMaxTotalOfDay(apk, day);
        Map<Integer, Integer> actionTotalMap = getActionTaskNumMap(markDayTasks);
        Integer[] prioritys = priorityActionMap.keySet().toArray(new Integer[0]);
        int lockTime = lockTime();
        if (filterTotal <= maxTotal) {
            int size = 0;
            for (int i = prioritys.length - 1; i >= 0; i--) {
                List<Integer> actionTypeList = priorityActionMap.get(prioritys[i]);
                if (actionTypeList != null && actionTypeList.size() > 0) {
                    String preDay = DateUtil.getPreSomeDay(2);
                    for (Integer actionType : actionTypeList) {
                        if (size < length) {
                            RetentFilter filter = new RetentFilter();
                            filter.setActionType(actionType);
                            filter.setLength(length - size);
                            filter.setMark(apk);
                            filter.setRd(day);
                            filter.setExecTimes(0);
                            // 次日留存时间需要大于24小时
                            if (preDay.equals(day.toString())) {
                                filter.setEndTime(DateUtil.getPreSomeDateStr(2));
                            } else {
                                //随机执行
                                if (retent24Filter == null || retent24Filter == 0) {
                                    filter.setDayRandom(getDayRandom(apk, actionType, day));
                                }
                            }
                            String lockKey = CacheKey.RETENT_QUERY_LIST_LOCK + ":" + day + ":" + apk;
                            if (redisClient.evalSetNx(lockKey, lockTime)) {
                                try {
                                    List<RetentTaskVo> retentTaskList = retentFilterService.selectRetentTaskInfoList(filter);
                                    Float randomValue = 0f;
                                    if (retentTaskList != null) {
                                        Integer currentDay = Integer.parseInt(DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd"));
                                        for (RetentTaskVo taskVo : retentTaskList) {
                                            taskVo.setCurrentDate(day.toString());
                                            taskVo.setMark(apk);
                                            updateImsiApkTime(taskVo.getImsi(), config);
                                            updateRetentFillRecord(taskVo, currentDay, config);
                                            //只修改次数
                                            updateRetentRecord(apk, taskVo.getImsi(), day.toString(), 1);
                                            randomValue = taskVo.getDayRandom();
                                        }
                                        updateQueryIndex(apk, day.toString(), actionType, randomValue);
                                        targetList.addAll(retentTaskList);
                                        size += retentTaskList.size();
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                } finally {
                                    redisClient.remove(lockKey);
                                }
                            }

                        } else {
                            break;
                        }
                    }
                }
            }
        } else {
            //  过滤的比总的大 需要区分优先级
            int dayLeft = getDayLeftNum(apk, day);
            String filterDayExecKey = CacheKey.ACTION_RETENT_REORD + apk + DateUtil.getDaysOfTime();
            Map<Integer, Integer> prioprityCanUseMap = initPrioprityCanUseMap(filterDayExecKey, day, apk, prioritys, priorityActionMap, actionTotalMap);
            int size = 0;
            for (int i = prioritys.length - 1; i >= 0; i--) {
                int prioryNum = 0;
                List<Integer> actionTypeList = priorityActionMap.get(prioritys[i]);
                if (actionTypeList != null && actionTypeList.size() > 0) {
                    String preDay = DateUtil.getPreSomeDay(2);
                    String endTime = DateUtil.getPreSomeDateStr(2);
                    for (Integer actionType : actionTypeList) {
                        if (size < length && prioprityCanUseMap.get(prioritys[i]) > prioryNum && dayLeft > size) {
                            Float randomValue = 0f;
                            RetentFilter filter = new RetentFilter();
                            filter.setActionType(actionType);
                            filter.setLength(length - size);
                            filter.setMark(apk);
                            filter.setRd(day);
                            if (preDay.equals(day.toString())) {
                                // 次日留存时间需要大于24小时
                                filter.setEndTime(endTime);
                            } else {
                                filter.setDayRandom(getDayRandom(apk, actionType, day));
                            }
                            filter.setExecTimes(0);
                            String lockKey = CacheKey.RETENT_QUERY_LIST_LOCK + ":" + day + ":" + apk;
                            if (redisClient.evalSetNx(lockKey, lockTime)) {
                                try {
                                    List<RetentTaskVo> retentTaskList = retentFilterService.selectRetentTaskInfoList(filter);
                                    if (retentTaskList != null) {
                                        for (RetentTaskVo taskInfo : retentTaskList) {
                                            taskInfo.setCurrentDate(day.toString());
                                            taskInfo.setMark(apk);
                                            randomValue = taskInfo.getDayRandom();
                                            updateRetentRecord(apk, taskInfo.getImsi(), day.toString(), 1);
                                        }
                                        targetList.addAll(retentTaskList);
                                        prioryNum += retentTaskList.size();
                                        size += retentTaskList.size();
                                        String field = getFieldKey(actionType, day);
                                        updateQueryIndex(apk, day.toString(), actionType, randomValue);
                                        redisClient.evalHincrby(filterDayExecKey, field, retentTaskList.size());
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                } finally {
                                    redisClient.remove(lockKey);
                                }
                            }

                        } else {
                            break;
                        }
                    }
                }
            }
        }
        return targetList;
    }


    /**
     * getFileterRetentNum:限制的留存总量
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private int getFileterRetentNum(List<MarkDayTask> markDayTasks, TreeMap<Integer, List<Integer>> priorityActionMap) {
        int filterTotal = 0;
        if (markDayTasks != null && markDayTasks.size() > 0) {
            for (MarkDayTask dayTask : markDayTasks) {
                filterTotal += dayTask.getTaskNum();
            }
        }
        return filterTotal;
    }


    /**
     * getActionTaskNumMap:
     *
     * @param markDayTasks
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private Map<Integer, Integer> getActionTaskNumMap(List<MarkDayTask> markDayTasks) {
        if (markDayTasks != null && !markDayTasks.isEmpty()) {
            Map<Integer, Integer> map = new HashMap<Integer, Integer>();
            for (MarkDayTask dayTask : markDayTasks) {
                map.put(dayTask.getActionType(), dayTask.getTaskNum());
            }
            return map;
        }
        return null;
    }

    /**
     * getApkMaxTotalOfDay:天最大占比的留存量
     *
     * @param apk
     * @param day
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private int getApkMaxTotalOfDay(String apk, Integer day) {
        String key = CacheKey.AR_TASK2 + DateUtil.getDaysOfTime() + apk;
        String total = redisClient.evalHget(key, day + "total");
        if (total == null) {
            return 0;
        }
        return Integer.parseInt(total);
    }


    /**
     * getFieldKey:天执行量filed
     *
     * @param actionType
     * @param day
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private String getFieldKey(Integer actionType, Integer day) {
        return actionType + "" + day + "exec";
    }

    /**
     * initPrioprityCanUseMap:初始化优先级能用量
     *
     * @param prioritys
     * @param priorityActionMap
     * @param actionTotalMap
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private Map<Integer, Integer> initPrioprityCanUseMap(String filterDayExecKey, Integer day, String apk, Integer[] prioritys, TreeMap<Integer, List<Integer>> priorityActionMap,
                                                         Map<Integer, Integer> actionTotalMap) {
        Map<Integer, Integer> prioprityCanUseMap = new HashMap<Integer, Integer>();
        for (int i = prioritys.length - 1; i >= 0; i--) {
            List<Integer> actionTypeList = priorityActionMap.get(prioritys[i]);
            if (actionTypeList != null && actionTypeList.size() > 0) {
                int canUseNum = 0;
                for (Integer actionType : actionTypeList) {
                    String field = getFieldKey(actionType, day);
                    String hasExec = redisClient.evalHget(filterDayExecKey, field);
                    if (StringUtils.isBlank(hasExec)) {
                        hasExec = "0";
                    }
                    Integer actionTypeNum = actionTotalMap.get(actionType);
                    if (actionTypeNum == null) {
                        actionTypeNum = 0;
                    }
                    int actionLeft = actionTypeNum - Integer.parseInt(hasExec);
                    canUseNum += actionLeft;
                }
                prioprityCanUseMap.put(prioritys[i], canUseNum);
            }
        }
        return prioprityCanUseMap;
    }


    /**
     * selectNofilterRetentTaskList:没有过滤规则的留存
     *
     * @param config
     * @param day
     * @param length
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private List<RetentTaskVo> selectNofilterRetentTaskList(ApkRetentConfig config, Integer day, Integer length, int times) {
        String apk = config.getMark();
        RetentQueryVo queryVo = new RetentQueryVo();
        queryVo.setTableDate(day.toString());
        queryVo.setSize(length);
        queryVo.setExectimes(times - 1);
        queryVo.setMark(apk);
        String preDay = DateUtil.getPreSomeDay(2);
        Integer filter = config.getRetent24Filter();
        if (preDay.equals(day.toString())) {
            // 次日留存时间需要大于24小时
            queryVo.setEndTime(DateUtil.getPreSomeDateStr(2));
        } else {
            //次日留存不需要随机 时间已经限制了
            if (filter == null || filter == 0) {
                Float randomValue = getDayRandom(apk, day, times);
                queryVo.setDayRandom(randomValue);
            }
        }
        String lockKey = CacheKey.RETENT_QUERY_LIST_LOCK + ":" + day + ":" + apk;
        List<RetentTaskVo> list = null;
        if (redisClient.evalSetNx(lockKey, lockTime())) {
            try {
                list = channelRetentDAO.getRetentTask(queryVo);
                if (list != null) {
                    Float dayRandom = 0f;
                    Integer currentDay = Integer.parseInt(DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd"));
                    for (RetentTaskVo taskVo : list) {
                        taskVo.setCurrentDate(day.toString());
                        dayRandom = taskVo.getDayRandom();
                        // 标记为已经做
                        updateImsiApkTime(taskVo.getImsi(), config);
                        updateRetentFillRecord(taskVo, currentDay, config);
                        // 只修改次数
                        updateRetentRecord(apk, taskVo.getImsi(), day.toString(), 1);
                    }
                    updateQueryIndex(apk, day.toString(), dayRandom, times);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                redisClient.remove(lockKey);
            }
        }

        return list;
    }


    /**
     * updateRetentFillRecord:更新次数和留存
     *
     * @param taskVo
     * @param currentDay
     * @param apkConfig
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void updateRetentFillRecord(RetentTaskVo taskVo, Integer currentDay, ApkRetentConfig apkConfig) {
        if (apkConfig != null) {
            try {
                Integer isFill = apkConfig.getIsFill();
                if (isFill != null && isFill == 1) {
                    ApkRetentLogVo record = new ApkRetentLogVo();
                    record.setImsi(taskVo.getImsi());
                    record.setApkId(apkConfig.getId());
                    String retentDay = taskVo.getCurrentDate();
                    record.setRetentDay(Integer.parseInt(retentDay));
                    record.setRetentMonth(Integer.parseInt(retentDay.substring(0, 6)));
                    record.setExecDay(currentDay);
                    record.setCurrentMonth(Integer.parseInt(currentDay.toString().substring(0, 6)));
                    record.setFile1Name(taskVo.getFile1Name());
                    record.setFile2Name(taskVo.getFile2Name());
                    record.setFile3Name(taskVo.getFile3Name());
                    record.setFile4Name(taskVo.getFile4Name());
                    record.setFile5Name(taskVo.getFile5Name());
                    record.setSettingSystem(taskVo.getSettingSystem());
                    retentFillExtimesBuffer.add(record);
                }
            } catch (Exception e) {
                logger.error("没表", e);
            }
        }
    }

    /**
     * 获取配置的actionType getActionType:
     *
     * @param apkId
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private TreeMap<Integer, List<Integer>> getActionType(Integer apkId) {
        List<RetentFilterConfig> retentFilterConfigList = retentFilterConfigService.selectRetentFilterConfigList(apkId);
        TreeMap<Integer, List<Integer>> map = new TreeMap<>();
        if (retentFilterConfigList != null && retentFilterConfigList.size() > 0) {
            for (RetentFilterConfig filterConfig : retentFilterConfigList) {
                Integer enable = filterConfig.getEnable();
                if (enable != null && enable == 1) {
                    Integer actionType = filterConfig.getActionType();
                    Integer priority = filterConfig.getPriority();
                    List<Integer> actionTypeList = map.get(priority);
                    if (actionTypeList == null) {
                        actionTypeList = new ArrayList<>();
                    }
                    actionTypeList.add(actionType);
                    map.put(priority, actionTypeList);
                }
            }
        }
        return map;
    }

    private UserClientInfo getUserClientInfo(RetentTaskVo taskVo) {
        UserClientInfo clientInfo;
        QueryUserClientVo userVo = new QueryUserClientVo();
        userVo.setImsi(taskVo.getImsi());
        userVo.setRd(taskVo.getCurrentDate());
        // 指定机型的时候需要区分mark
        userVo.setMark(taskVo.getMark());
        clientInfo = userClientInfoLogService.selectClientInfo(userVo);
        if (clientInfo != null) {
            return clientInfo;
        }
        // 往前一天查询
        try {
            userVo.setRd(DateUtil.getPreDay(2, taskVo.getCurrentDate()));
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
        clientInfo = userClientInfoLogService.selectClientInfo(userVo);
        if (clientInfo != null) {
            return clientInfo;
        }
        // 如果没查到 则不区分机型了
        userVo.setMark(null);
        clientInfo = userClientInfoLogService.selectClientInfo(userVo);
        if (clientInfo != null) {
            return clientInfo;
        }
        userVo.setRd(taskVo.getCurrentDate());
        clientInfo = userClientInfoLogService.selectClientInfo(userVo);
        return clientInfo;
    }

    /**
     * updateRetentFillRecord:只更新留存次数
     *
     * @param apkId
     * @param imsi
     * @param currentDay
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void updateRetentFillRecord(Integer apkId, String imsi, Integer currentDay) {
        try {
            ApkRetentLogVo record = new ApkRetentLogVo();
            record.setImsi(imsi);
            record.setApkId(apkId);
            record.setExecDay(currentDay);
            record.setCurrentMonth(Integer.parseInt(currentDay.toString().substring(0, 6)));
            retentFillExtimesBuffer.add(record);
        } catch (Exception e) {
            logger.error("没表", e);
        }
    }

    /**
     * @param mark
     * @param day
     * @param randomValue
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void updateQueryIndex(String mark, String day, Float randomValue, Integer execTimes) {
        String indexKey = CacheKey.R_I_D_R;
        String fieldKey = mark + day + execTimes;
        if (day.equals(DateUtil.getCurrentDateStr())) {
            redisClient.putHash(indexKey, fieldKey, 0);
        } else {
            redisClient.putHash(indexKey, fieldKey, randomValue);
        }
    }

    /**
     * updateQueryIndex:更新任务下标
     *
     * @param mark
     * @param day
     * @param actionType
     * @param randomValue
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void updateQueryIndex(String mark, String day, Integer actionType, Float randomValue) {
        String indexKey = CacheKey.R_I_D_R;
        String fieldKey = mark + day + actionType;
        if (day.equals(DateUtil.getCurrentDateStr())) {
            redisClient.putHash(indexKey, fieldKey, 0);
        } else {
            redisClient.putHash(indexKey, fieldKey, randomValue);
        }
    }

    private Float getDayRandom(String apk, Integer day, int execTimes) {
        return getDayRandom(apk, null, day, execTimes);
    }


    /**
     * updateImsiApkTime:
     *
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private void updateImsiApkTime(String imsi, ApkRetentConfig apkConfig) {
        if (apkConfig != null) {
            Integer base = apkConfig.getBase();
            String key = CacheKey.A_IMSI + imsi;
            String value = null;
            Integer apkId = apkConfig.getId();
            if (base != null && base == ApkBaseEnum.OTHER.getType()) {
                value = apkId + "|" + System.currentTimeMillis() + "|";
            } else if (apkId != null) {
                value = base + "|" + System.currentTimeMillis() + "|";
            } else {
                return;
            }
            redisClient.evalAppend(key, value);
        }
    }

    private Float getDayRandom(String apk, Integer actionType, Integer day, Integer execTimes) {
        String indexKey = CacheKey.R_I_D_R;
        String fieldKey = apk + day + execTimes;
        if (actionType != null) {
            fieldKey = apk + day + actionType + execTimes;
        }
        Float randomValue = 0f;
        Object randomV = redisClient.getHash(indexKey, fieldKey);
        if (randomV != null) {
            randomValue = Float.parseFloat(randomV.toString());
        }
        return randomValue;
    }


    /**
     * getDayLeftNum:天留存剩余量
     *
     * @param apk
     * @param day
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private int getDayLeftNum(String apk, Integer day) {
        String dayHasExecKey = CacheKey.AR_TASK2 + dayOfTime + apk;
        String dayTotal = redisClient.evalHget(dayHasExecKey, day + "total");
        String dayExec = redisClient.evalHget(dayHasExecKey, day + "exectotal");
        int dayLeft = 0;
        if (StringUtils.isNotBlank(dayTotal)) {
            if (StringUtils.isNotBlank(dayExec)) {
                dayLeft = Integer.parseInt(dayTotal) - Integer.parseInt(dayExec);
            } else {
                dayLeft = Integer.parseInt(dayTotal);
            }
        }
        return dayLeft;
    }

}
